package org.geektimes.projects.user.support.transaction;

import org.geektimes.base.proxy.Advice;
import org.geektimes.base.proxy.MethodProcessor;
import org.geektimes.base.util.ReflectionUtils;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* 事物通知
 * ///todo 只实现一个简单的事物 ///如嵌套事物，不同隔离级别，不同传播级别的处理待实现
* @author : lsq
*/
public class TransactionAdvice implements Advice {


	private final static Logger logger = Logger.getLogger(TransactionAdvice.class.getName());

	private EntityManager entityManager;


	private Map<Method , Class<? extends Throwable>> rollbackMap = new HashMap<>();



	public  TransactionAdvice(Class clazz , EntityManager entityManager){
		this.entityManager = entityManager;
		List<Method> methods = ReflectionUtils.getMethods(clazz, Object.class);
		for (Method method : methods){
			Transaction annotation = method.getAnnotation(Transaction.class);
			if (annotation != null){
				rollbackMap.put(method , annotation.rollback());
			}
		}
	}



	@Override
	public Object invoke(MethodProcessor methodProcessor) throws Throwable {
		EntityTransaction transaction = entityManager.getTransaction();
		String methodName = methodProcessor.getMethod().toGenericString();
		transaction.begin();
		logger.log(Level.INFO ,  methodName+"start transaction");
		Object value = null;
		try {
			value = methodProcessor.invoke();
			transaction.commit();
			logger.log(Level.INFO , methodName+"commit transaction");
		}catch (Throwable e){
			Class<? extends Throwable> aClass = rollbackMap.get(methodProcessor.getMethod());
			Throwable cause = e.getCause();
			if (cause instanceof InvocationTargetException){
				cause = cause.getCause();
			}
			if (aClass.isInstance(cause) ){
				transaction.rollback();
				logger.log(Level.INFO , methodName+"rollback transaction");
			}
		}
		return value;
	}
}
